/*  File Name	: ProcessManager.cpp
 *  Author		: Pang Jiacai
 *	 E-mail		: 823960168@qq.com
 *  Created on	: 2012-6-29
 *  Description:
 */

#include "ProcessManager.h"
#include "ComDefine.h"
#include "Reactor.h"
#include <wait.h>

Process::Process(std::string type,int line)
{
	this->line_ = line;
	this->type_  = type;
	this->notify_exit_ = NULL;
	this->child_id_ = -1;
}

Process::~Process()
{

}

int Process::spawn(char *path,char *argv[],std::string working_dir,bool inherit_env)
{
	//std::cout<<"[ppid:"<<getppid()<<"][pid:"<<getpid()<<"]Process::spawn-->1"<<std::endl;
	this->child_id_ = ::fork();
	//std::cout<<"[ppid:"<<getppid()<<"][pid:"<<getpid()<<"]Process::spawn-->2 child_id:"<<child_id_<<std::endl;
	if(this->child_id_==0)
	{
		if(!working_dir.empty())
		{
			::chdir(working_dir.c_str());
		}

		for (int i = ::sysconf (_SC_OPEN_MAX) - 1; i >= 0; i--)
		{
			if (i == STDIN_FILENO || i == STDOUT_FILENO || i == STDERR_FILENO)
			{
				continue;
			}
			::fcntl(i, F_SETFD, FD_CLOEXEC);
		}

		if(inherit_env)
		{
			if(::execvp(path, argv)<0)
			{
				std::cout<<"[ppid:"<<getppid()<<"][pid:"<<getpid()<<"]Process::spawn-->execvp fail"<<std::endl;
			}
		}
		//::execve(path, argv,NULL);
		return 0;
	}
	return this->child_id_;
}

int Process::terminate(int sig)
{
	return ::kill(this->child_id_,sig);
}

const int Process::pid(void)
{
	return this->child_id_;
}

const int Process::line(void)
{
	return this->line_;
}

std::string Process::type(void)
{
	return this->type_;
}

EventHandler* Process::notify_handler(EventHandler* eh)
{
	if(NULL!=eh)
	{
		this->notify_exit_ = eh;
	}
	return this->notify_exit_;
}
/////////////////////////////////////////////////////////////////////

ProcessManager::ProcessManager(Reactor *reactor)
{
	this->process_map_.clear();
	this->open(reactor);
}

ProcessManager::~ProcessManager()
{
	for(ProcessMap::iterator iter= this->process_map_.begin();
			iter!=this->process_map_.end();iter++)
	{
		DELETE(iter->second);
	}
}

int ProcessManager::open(Reactor *reactor)
{
	if(NULL!=reactor)
	{
		this->reactor(reactor);
		if(reactor->register_signal(SIGCHLD,this)>=0)
		{
			LOG((INFO_LOG,"ProcessManager::open [%d]",SIGCHLD));
			return 0;
		}
	}
	return -1;
}

Process* ProcessManager::find_process(int pid)
{
	LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,NULL);
	ProcessMap::iterator iter = this->process_map_.find(pid);
	if(iter==this->process_map_.end())
	{
		return NULL;
	}
	return iter->second;
}

int ProcessManager::remove_process(int pid)
{
	LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,-1);
	ProcessMap::iterator iter = this->process_map_.find(pid);
	if(iter!=this->process_map_.end())
	{
		DELETE(iter->second);
		this->process_map_.erase(pid);
		return 0;
	}
	return -1;
}

int ProcessManager::spawn(char *program,char *argv[],
		std::string working_dir,bool inherit_env)
{
	Process *process = new Process(argv[1],atoi(argv[2]));
	if(NULL==process)
	{
		LOG((ERROR_LOG,"ProcessManager::spawn null point"));
		return -1;
	}

	pid_t pid = process->spawn(program,argv,working_dir,inherit_env);
	if (pid == -1 || pid == 0)
	{
		LOG((ERROR_LOG,"ProcessManager::spawn pid:[%d]",pid));
		DELETE(process);
	}
	else
	{
		LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,-1);
	    if(this->process_map_.insert(std::pair<pid_t,Process*>(pid,process)).second==false)
	    {
	    	process->terminate(9);
	    	DELETE(process);
	    	LOG((ERROR_LOG,"ProcessManager::spawn insert error:[%d]",pid));
	    	return -1;
	    }
	}
	return pid;
}

int ProcessManager::stop_all_child_process(void)
{
	LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,-1);
	ProcessMap::iterator iter=this->process_map_.begin();
	for(;iter!=this->process_map_.end();iter++)
	{
		if(iter->second!=NULL)
		{
			LOG((INFO_LOG,"terminate pid:[%d] typd:[%s]",
					iter->second->pid(),iter->second->type().c_str()))
			iter->second->terminate(SIGNAL_GAME_STOP);
		}
	}
	return 0;
}

int ProcessManager::terminate(pid_t pid,int sig)
{
	if(this->process_map_.find(pid)==this->process_map_.end())
	{
		return -1;
	}
	return ::kill(pid,sig);
}

int ProcessManager::handle_signal(int signum)
{
	LOG((INFO_LOG,"ProcessManager::handle_signal[%d]",signum));
	if(signum==SIGCHLD)
	{
		int pid = this->wait();
		LOG((INFO_LOG,"ProcessManager::handle_signal pid[%d]",pid));
		if(pid==-1 || pid==0)
		{
			return 0;
		}

		EventHandler *eh = NULL;
		{
			LockGuardReturn(RT_MUTEX, lock, this->rt_mutex_, -1);
			ProcessMap::iterator iter = this->process_map_.find(pid);
			if (iter== this->process_map_.end()
					||iter->second->notify_handler()==NULL)
			{
				return 0;
			}
			eh = iter->second->notify_handler();
		}
		if(NULL!=eh)
		{
			eh->handle_exit(pid);
		}
	}
	return 0;
}

int ProcessManager::register_handler(EventHandler *eh,int pid)
{
	LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,-1);
	ProcessMap::iterator iter =this->process_map_.find(pid);
	if(eh==NULL || iter==this->process_map_.end())
	{
		LOG((ERROR_LOG,"ProcessManager::register_handler[%@]",eh));
		return -1;
	}
	iter->second->notify_handler(eh);
	return 0;
}

int ProcessManager::wait(void)
{
	int *status = 0;
	int exit_pid = ::waitpid (WAIT_ANY,status, WNOHANG);
	return exit_pid;
}

int ProcessManager::has_child(void)
{
	LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,-1);
	if(this->process_map_.size()>0)
	{
		return this->process_map_.size();
	}
	return 0;
}
